package server

import (
	"bufio"
	"fmt"
	"net"
	"strings"
	"sync"
)

type ConnCtx struct {
	User string
}

type Server struct {
	Ip               string
	Port             int
	UserInfoMap      map[string]*UserInfo
	UserInfoMapLock  sync.RWMutex
	ConnCtxMap       map[net.Conn]*ConnCtx
	ConnCtxMapLock   sync.RWMutex
	GroupInfoMap     map[string]*GroupInfo
	GroupInfoMapLock sync.RWMutex
}

//New 创建服务器
func New(ip string, port int) *Server {
	return &Server{
		Ip:           ip,
		Port:         port,
		UserInfoMap:  make(map[string]*UserInfo),
		ConnCtxMap:   make(map[net.Conn]*ConnCtx),
		GroupInfoMap: make(map[string]*GroupInfo),
	}
}

//Start 启动服务器
func (srv *Server) Start() {
	//socket listen
	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", srv.Ip, srv.Port))
	if err != nil {
		return
	}

	//close listen socket
	defer listener.Close()

	//循环监听accept
	for {
		conn, err := listener.Accept()
		if err != nil {
			logError("listener accept err:", err)
			continue
		}

		go srv.handleConn(conn)
	}
}

// 处理每个客户端连接
func (srv *Server) handleConn(conn net.Conn) {
	logInfo(fmt.Sprintf("[建立连接] 服务端=%v，客户端=%v", conn.LocalAddr(), conn.RemoteAddr()))

	srv.ConnCtxMap[conn] = &ConnCtx{}

	//接收客户端发送的消息
	go func() {
		scanner := bufio.NewScanner(conn)
		for scanner.Scan() {
			msg := scanner.Text()
			srv.parseClientMsg(conn, msg)
		}
		//断开连接
		me := srv.getConnUser(conn)
		//如果已经登录了
		if me != "" {
			srv.UserInfoMapLock.Lock()
			srv.UserInfoMap[me].Conn = nil
			srv.UserInfoMapLock.Unlock()
			//通知好友下线
			srv.notifyFriendOffline(me)
		}
		delete(srv.ConnCtxMap, conn)
		logInfo(fmt.Sprintf("[断开连接] (%s) 服务端=%v，客户端=%v", me, conn.LocalAddr(), conn.RemoteAddr()))
	}()

	//阻塞当前handler
	select {}
}

//入口：处理客户端消息
func (srv *Server) parseClientMsg(conn net.Conn, msg string) {
	tokens := strings.Split(msg, "|")
	if len(tokens) == 0 {
		return
	}

	cmd := tokens[0]
	switch cmd {
	case "signin":
		srv.onSignInMsg(conn, msg)
	case "signup":
		srv.onSignUpMsg(conn, msg)
	case "addfriend":
		srv.onAddFriend(conn, msg)
	case "delfriend":
		srv.onDelFriend(conn, msg)
	case "getfriendstatus":
		srv.onGetFriendStatus(conn)
	case "usermsg":
		srv.onUserMsg(conn, msg)
	case "creategroup":
		srv.onCreateGroup(conn, msg)
	case "joingroup":
		srv.onJoinGroup(conn, msg)
	case "exitgroup":
		srv.onExitGroup(conn, msg)
	case "groupmsg":
		srv.onGroupMsg(conn, msg)
	case "removeaccount":
		srv.onRemoveAccount(conn)
	}
}

//处理注册消息
func (srv *Server) onSignInMsg(conn net.Conn, msg string) {
	tokens := strings.Split(msg, "|")
	if len(tokens) != 3 {
		sendMsg(conn, "signinfail|注册格式错误")
		return
	}

	//提取参数
	user := tokens[1]
	passwd := tokens[2]

	//检查参数
	if user == "" || passwd == "" {
		sendMsg(conn, "signinfail|用户名与密码不能为空")
		return
	}

	//判断用户是否已注册
	if _, ok := srv.UserInfoMap[user]; ok {
		sendMsg(conn, "signinfail|该用户已经注册")
		return
	}

	//写入数据
	userInfo := NewUserInfo(user, passwd)
	srv.UserInfoMapLock.Lock()
	srv.UserInfoMap[user] = userInfo
	srv.UserInfoMapLock.Unlock()

	logInfo(fmt.Sprintf("[注册新用户] user=%s,passwd=%s", user, passwd))

	//返回消息
	sendMsg(conn, "signinok")

	//立即登录
	srv.onSignUpMsg(conn, fmt.Sprintf("signup|%s|%s", user, passwd))
}

//处理登录消息
func (srv *Server) onSignUpMsg(conn net.Conn, msg string) {
	tokens := strings.Split(msg, "|")
	if len(tokens) != 3 {
		sendMsg(conn, "signupfail|登录格式错误")
		return
	}

	//提取参数
	user := tokens[1]
	passwd := tokens[2]

	//比对登录信息
	srv.UserInfoMapLock.RLock()
	defer srv.UserInfoMapLock.RUnlock()
	if u, ok := srv.UserInfoMap[user]; ok {
		if srv.UserInfoMap[user].Conn != nil {
			sendMsg(conn, "signupfail|账号已登录")
			return
		}
		if u.Passwd == passwd {
			//关联当前conn与用户
			srv.ConnCtxMap[conn].User = u.Name
			srv.UserInfoMap[user].Conn = conn
			//返回消息
			sendMsg(conn, "signupok|"+user)
			//通知好友上线
			srv.notifyFriendOnline(user)
			logInfo(fmt.Sprintf("[登录] %s", user))
		} else {
			sendMsg(conn, "signupfail|密码错误")
		}
	} else {
		sendMsg(conn, "signupfail|用户不存在")
	}
}

//处理添加好友消息
func (srv *Server) onAddFriend(conn net.Conn, msg string) {
	//检查登录状态
	if !srv.isConnLogined(conn) {
		sendMsg(conn, "addfriendfail|你没有登录")
		return
	}

	//检查格式
	tokens := strings.Split(msg, "|")
	if len(tokens) != 2 {
		sendMsg(conn, "addfriendfail|添加好友格式错误")
		return
	}

	//提取参数
	user := tokens[1]

	//检查用户是否存在
	if !srv.isUserExist(user) {
		sendMsg(conn, "addfriendfail|用户不存在")
		return
	}

	//获取自己的用户名
	me := srv.getConnUser(conn)

	//不能添加自己
	if me == user {
		sendMsg(conn, "addfriendfail|不能添加自己为好友")
		return
	}

	srv.UserInfoMapLock.Lock()
	defer srv.UserInfoMapLock.Unlock()
	//判断是否已经添加
	if _, ok := srv.UserInfoMap[me].Friends[user]; ok {
		sendMsg(conn, "addfriendfail|"+user+"已经是你的好友了")
		return
	}
	srv.UserInfoMap[me].Friends[user] = struct{}{}

	//返回消息
	sendMsg(conn, "addfriendok")
	logInfo(fmt.Sprintf("[添加好友] %s 添加 %s", me, user))
}

//处理删除好友消息
func (srv *Server) onDelFriend(conn net.Conn, msg string) {
	//检查登录状态
	if !srv.isConnLogined(conn) {
		sendMsg(conn, "delfriendfail|你没有登录")
		return
	}

	//检查格式
	tokens := strings.Split(msg, "|")
	if len(tokens) != 2 {
		sendMsg(conn, "delfriendfail|添加好友格式错误")
		return
	}

	//提取参数
	user := tokens[1]

	//检查用户是否存在
	if !srv.isUserExist(user) {
		sendMsg(conn, "delfriendfail|用户不存在")
		return
	}

	//获取自己的用户名
	me := srv.getConnUser(conn)

	//不能删除自己
	if me == user {
		sendMsg(conn, "delfriendfail|不能删除自己")
		return
	}

	srv.UserInfoMapLock.Lock()
	defer srv.UserInfoMapLock.Unlock()
	//判断是否已经添加
	if _, ok := srv.UserInfoMap[me].Friends[user]; !ok {
		sendMsg(conn, "delfriendfail|"+user+"不是你的好友")
		return
	}
	delete(srv.UserInfoMap[me].Friends, user)

	//返回消息
	sendMsg(conn, "delfriendok")
	logInfo(fmt.Sprintf("[删除好友] %s 删除 %s", me, user))
}

//处理查询好友在线情况消息
func (srv *Server) onGetFriendStatus(conn net.Conn) {
	//检查登录状态
	if !srv.isConnLogined(conn) {
		sendMsg(conn, "addfriendfail|你没有登录")
		return
	}

	//获取自己的用户名
	me := srv.getConnUser(conn)

	onlineFriends := make([]string, 0)
	offlineFriends := make([]string, 0)
	for f := range srv.UserInfoMap[me].Friends {
		if srv.UserInfoMap[f].Conn == nil {
			offlineFriends = append(offlineFriends, f)
		} else {
			onlineFriends = append(onlineFriends, f)
		}
	}

	res := fmt.Sprintf("friendstatus|%s|%s", strings.Join(onlineFriends, ","), strings.Join(offlineFriends, ","))
	sendMsg(conn, res)

	logInfo(fmt.Sprintf("[查询好友在线情况] 查询人：%s，返回：%s", me, res))
}

func (srv *Server) onUserMsg(conn net.Conn, msg string) {
	//检查登录状态
	if !srv.isConnLogined(conn) {
		sendMsg(conn, "usermsgfail|你没有登录")
		return
	}

	//检查格式
	tokens := strings.Split(msg, "|")
	if len(tokens) != 3 {
		sendMsg(conn, "usermsgfail|私聊格式错误")
		return
	}

	//提取参数
	user := tokens[1]
	content := tokens[2]

	//判断该用户是否存在
	if !srv.isUserExist(user) {
		sendMsg(conn, "usermsgfail|用户不存在")
		return
	}

	//获取自己的用户名
	me := srv.getConnUser(conn)

	//找到对方的conn
	friendConn := srv.UserInfoMap[user].Conn

	//判断是否在线
	if friendConn == nil {
		sendMsg(conn, "usermsgfail|用户不在线")
		return
	}

	//发送消息
	sendMsg(friendConn, fmt.Sprintf("recvusermsg|%s|%s", me, content))

	logInfo(fmt.Sprintf("[私聊] %s -> %s : %s", me, user, content))
}

func (srv *Server) onCreateGroup(conn net.Conn, msg string) {
	//检查登录状态
	if !srv.isConnLogined(conn) {
		sendMsg(conn, "creategroupfail|你没有登录")
		return
	}

	//检查格式
	tokens := strings.Split(msg, "|")
	if len(tokens) != 2 {
		sendMsg(conn, "creategroupfail|创建群聊格式错误")
		return
	}

	//提取参数
	group := tokens[1]

	//检查群聊是否存在
	if srv.isGroupExist(group) {
		sendMsg(conn, "creategroupfail|该群聊已存在")
		return
	}

	//获取自己的用户名
	me := srv.getConnUser(conn)

	//创建群聊
	groupInfo := NewGroupInfo(group, me)
	srv.GroupInfoMapLock.Lock()
	srv.GroupInfoMap[group] = groupInfo
	srv.GroupInfoMapLock.Unlock()

	//自己加入群聊
	srv.UserInfoMap[me].Groups[group] = struct{}{}
	groupInfo.Members[me] = struct{}{}

	//返回消息
	sendMsg(conn, "creategroupok")
	logInfo(fmt.Sprintf("[创建群聊] %s 创建了群 %s", me, group))
}

func (srv *Server) onJoinGroup(conn net.Conn, msg string) {
	//检查登录状态
	if !srv.isConnLogined(conn) {
		sendMsg(conn, "joingroupfail|你没有登录")
		return
	}

	//检查格式
	tokens := strings.Split(msg, "|")
	if len(tokens) != 2 {
		sendMsg(conn, "joingroupfail|加入群聊格式错误")
		return
	}

	//提取参数
	group := tokens[1]

	//检查群聊是否存在
	if !srv.isGroupExist(group) {
		sendMsg(conn, "joingroupfail|群聊不存在")
		return
	}

	//获取自己的用户名
	me := srv.getConnUser(conn)

	//检查自己是否已加入该群聊
	if _, ok := srv.UserInfoMap[me].Groups[group]; ok {
		sendMsg(conn, "joingroupfail|你已加入该群聊")
		return
	}

	//自己加入群聊
	srv.UserInfoMap[me].Groups[group] = struct{}{}
	srv.GroupInfoMap[group].MembersLock.Lock()
	srv.GroupInfoMap[group].Members[me] = struct{}{}
	srv.GroupInfoMap[group].MembersLock.Unlock()

	//返回消息
	sendMsg(conn, "joingroupok")
	logInfo(fmt.Sprintf("[加入群聊] %s 加入了群 %s", me, group))
}

func (srv *Server) onExitGroup(conn net.Conn, msg string) {
	//检查登录状态
	if !srv.isConnLogined(conn) {
		sendMsg(conn, "exitgroupfail|你没有登录")
		return
	}

	//检查格式
	tokens := strings.Split(msg, "|")
	if len(tokens) != 2 {
		sendMsg(conn, "exitgroupfail|退出群聊格式错误")
		return
	}

	//提取参数
	group := tokens[1]

	//检查群聊是否存在
	if !srv.isGroupExist(group) {
		sendMsg(conn, "exitgroupfail|群聊不存在")
		return
	}

	//获取自己的用户名
	me := srv.getConnUser(conn)

	//检查自己是否已加入该群聊
	if _, ok := srv.UserInfoMap[me].Groups[group]; !ok {
		sendMsg(conn, "exitgroupfail|你不在该群聊中")
		return
	}

	//退出该群聊
	delete(srv.UserInfoMap[me].Groups, group)
	srv.GroupInfoMap[group].MembersLock.Lock()
	delete(srv.GroupInfoMap[group].Members, me)
	srv.GroupInfoMap[group].MembersLock.Unlock()

	//返回消息
	sendMsg(conn, "exitgroupok")
	logInfo(fmt.Sprintf("[退出群聊] %s 退出了群 %s", me, group))

	//如果该群中没有成员了，则删除该群
	srv.cleanGroup(group)
}

func (srv *Server) onGroupMsg(conn net.Conn, msg string) {
	//检查登录状态
	if !srv.isConnLogined(conn) {
		sendMsg(conn, "groupmsgfail|你没有登录")
		return
	}

	//检查格式
	tokens := strings.Split(msg, "|")
	if len(tokens) != 3 {
		sendMsg(conn, "groupmsgfail|发送群聊格式错误")
		return
	}

	//提取参数
	group := tokens[1]
	content := tokens[2]

	//判断该群是否存在
	if !srv.isGroupExist(group) {
		sendMsg(conn, "groupmsgfail|群不存在")
		return
	}

	//获取自己的用户名
	me := srv.getConnUser(conn)

	//发送群聊消息
	srv.sendGroupMsg(me, group, content)

	logInfo(fmt.Sprintf("[群聊] %s -> %s : %s", me, group, content))
}

func (srv *Server) onRemoveAccount(conn net.Conn) {
	//检查登录状态
	if !srv.isConnLogined(conn) {
		sendMsg(conn, "removeaccountfail|你没有登录")
		return
	}

	//获取自己的用户名
	me := srv.getConnUser(conn)
	//从群中删除me
	for g := range srv.UserInfoMap[me].Groups {
		delete(srv.GroupInfoMap[g].Members, me)
	}
	//从用户好友中删除me
	for _, u := range srv.UserInfoMap {
		delete(u.Friends, me)
	}
	//删除我的数据
	delete(srv.UserInfoMap, me)
	//重置当前conn
	srv.ConnCtxMap[conn].User = ""

	//返回消息
	sendMsg(conn, "removeaccountok")

	logInfo(fmt.Sprintf("[注销用户] %s", me))
}

//通知好友上线
func (srv *Server) notifyFriendOnline(me string) {
	srv.UserInfoMapLock.RLock()
	defer srv.UserInfoMapLock.RUnlock()

	msg := fmt.Sprintf("notifyfriendonline|%s", me)
	for _, user := range srv.UserInfoMap {
		//me是user的好友，且user在线
		if _, ok := user.Friends[me]; ok && user.Conn != nil {
			sendMsg(user.Conn, msg)
		}
	}
}

//通知好友下线
func (srv *Server) notifyFriendOffline(me string) {
	srv.UserInfoMapLock.RLock()
	defer srv.UserInfoMapLock.RUnlock()

	msg := fmt.Sprintf("notifyfriendoffline|%s", me)
	for _, user := range srv.UserInfoMap {
		//me是user的好友，且user在线
		if _, ok := user.Friends[me]; ok && user.Conn != nil {
			sendMsg(user.Conn, msg)
		}
	}
}

func (srv *Server) isGroupExist(group string) bool {
	_, ok := srv.GroupInfoMap[group]
	return ok
}

func (srv *Server) cleanGroup(group string) {
	srv.GroupInfoMap[group].MembersLock.RLock()
	defer srv.GroupInfoMap[group].MembersLock.RUnlock()
	if len(srv.GroupInfoMap[group].Members) == 0 {
		srv.GroupInfoMapLock.Lock()
		defer srv.GroupInfoMapLock.Unlock()
		delete(srv.GroupInfoMap, group)
		logInfo(fmt.Sprintf("[销毁群聊] %s", group))
	}
}

func (srv *Server) sendGroupMsg(me string, group string, content string) {
	srv.GroupInfoMapLock.RLock()
	defer srv.GroupInfoMapLock.RUnlock()

	msg := fmt.Sprintf("recvgroupmsg|%s|%s|%s", group, me, content)
	srv.GroupInfoMap[group].MembersLock.RLock()
	defer srv.GroupInfoMap[group].MembersLock.RUnlock()
	for u, _ := range srv.GroupInfoMap[group].Members {
		if u != me {
			if conn := srv.UserInfoMap[u].Conn; conn != nil {
				sendMsg(conn, msg)
			}
		}
	}
}

//判断某个用户是否存在
func (srv *Server) isUserExist(user string) bool {
	srv.UserInfoMapLock.RLock()
	defer srv.UserInfoMapLock.RUnlock()
	_, ok := srv.UserInfoMap[user]
	return ok
}

//获取该conn对应的用户名
func (srv *Server) getConnUser(conn net.Conn) string {
	srv.ConnCtxMapLock.RLock()
	defer srv.ConnCtxMapLock.RUnlock()
	return srv.ConnCtxMap[conn].User
}

//判断该conn是否已登录
func (srv *Server) isConnLogined(conn net.Conn) bool {
	srv.ConnCtxMapLock.RLock()
	defer srv.ConnCtxMapLock.RUnlock()
	_, ok := srv.ConnCtxMap[conn]
	return ok
}

func sendMsg(conn net.Conn, msg string) {
	conn.Write([]byte(msg + "\n"))
}

//--------------------------------------------------------------------

// 打印日志
func logInfo(msg ...interface{}) {
	fmt.Println(msg...)
}

// 打印错误信息
func logError(errMsg ...interface{}) {
	fmt.Println(errMsg...)
}
